package org.mj.im.server.netty.processor.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.AttributeKey;
import org.mj.im.common.constants.IMConstants;
import org.mj.im.common.distribute.DistributeCacheService;
import org.mj.im.common.enums.IMCmdType;
import org.mj.im.common.jwt.JwtUtils;
import org.mj.im.common.model.IMLoginInfo;
import org.mj.im.common.model.IMSendInfo;
import org.mj.im.common.model.IMSessionInfo;
import org.mj.im.server.cache.UserChannelContextCache;

import org.mj.im.server.netty.processor.MessageProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
public class LoginProcessor implements MessageProcessor<IMLoginInfo> {
    private final Logger logger = LoggerFactory.getLogger(LoginProcessor.class);
    @Value("${jwt.accessToken.secret}")
    private String accessTokenSecret;

    @Value("${server.id}")
    private Long serverId;

    private final DistributeCacheService distributeCacheService;

    @Autowired
    public LoginProcessor(DistributeCacheService distributeCacheService) {
        this.distributeCacheService = distributeCacheService;
    }

    @Override
    public synchronized void process(ChannelHandlerContext ctx, IMLoginInfo data) {
        if (!JwtUtils.checkToken(data.getAccessToken(),accessTokenSecret)){
            ctx.channel().close();
            logger.warn("LoginProcessor.process|用户登录信息校验未通过,强制用户下线,token:{}", data.getAccessToken());
        }
        String info = JwtUtils.getInfo(data.getAccessToken());
        IMSessionInfo sessionInfo = JSON.parseObject(info, IMSessionInfo.class);
        if (sessionInfo == null){
            logger.warn("LoginProcessor.process|转化后的SessionInfo为空");
            return;
        }
        Long userId = sessionInfo.getUserId();
        Integer terminal = sessionInfo.getTerminal();
        logger.info("LoginProcessor.process|用户登录, userId:{}", userId);
        ChannelHandlerContext context = UserChannelContextCache.get(userId, terminal);
        //判断当前连接的id不同，则表示当前用户已经在异地登录
        if (context != null && !context.channel().id().equals(ctx.channel().id())){
            //不允许用户在同一种终端，登录多个设备
            IMSendInfo<String> imSendInfo = new IMSendInfo<>(IMCmdType.FORCE_LOGOUT.getCode(), "您已在其他地方登录，将被强制下线");
            context.channel().writeAndFlush(imSendInfo);
            logger.info("LoginProcessor.process|异地登录，强制下线，userid:{}", userId);
        }
        // 缓存用户和channel的关系
        UserChannelContextCache.addChannel(userId,terminal,context);
        // 设置用户相关属性
        AttributeKey<Long> userIdKey = AttributeKey.valueOf(IMConstants.USER_ID);
        ctx.channel().attr(userIdKey).set(userId);
        // 设置用户的终端
        AttributeKey<Integer> terminalTypeKey = AttributeKey.valueOf(IMConstants.TERMINAL_TYPE);
        ctx.channel().attr(terminalTypeKey).set(terminal);
        // 记录用户登录的信息
        String redisKey = String.join(IMConstants.REDIS_KEY_SPLIT,IMConstants.IM_USER_SERVER_ID,userId.toString(),terminal.toString());
        distributeCacheService.set(redisKey,serverId,IMConstants.ONLINE_TIMEOUT_SECONDS, TimeUnit.SECONDS);

        // 返回结果
        IMSendInfo<Object> imSendInfo = new IMSendInfo<>();
        imSendInfo.setCmd(IMCmdType.LOGIN.getCode());
        ctx.channel().writeAndFlush(imSendInfo);
    }

    @Override
    public IMLoginInfo transFrom(Object object) {
        Map<?,?> map = (Map<?,?>) object;
        return BeanUtil.fillBeanWithMap(map,new IMLoginInfo(),false);
    }
}
